// test_integration.c
#include "test_framework.h"
#include "../hisi_l0_mem_pool.h"
#include "test_common.h"
#include "test_registration.h"

/**
 * test_mixed_allocation_patterns - Tests mixed allocation/deallocation patterns
 * 
 * This test verifies:
 * 1. Mixed allocation patterns with varying object sizes
 * 2. Memory fragmentation handling
 * 3. Gap filling in fragmented memory
 * 4. Complex allocation/deallocation sequences
 * 5. Proper error handling for all allocation operations
 * 
 * Tests realistic allocation patterns and fragmentation handling with strict validation
 */
static int test_mixed_allocation_patterns(struct seq_file *m)
{
    struct test_context *ctx = get_test_context();
    void *ptrs[20];
    size_t sizes[] = {32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};
    int i, j;
    int passed = 0, total = 0;
    bool has_failures = false;
    unsigned char *data;
    bool content_valid;
    
    // Initialize ptrs array
    memset(ptrs, 0, sizeof(ptrs));
    
    // Mixed allocation pattern - allocate objects of varying sizes
    for (i = 0; i < ARRAY_SIZE(sizes) && i < 10; i++) {
        total++;
        ptrs[i] = l0_kmalloc(sizes[i], ctx->test_node_id);
        if (ptrs[i]) {
            // Fill with pattern
            memset(ptrs[i], i & 0xFF, sizes[i] > 1024 ? 1024 : sizes[i]);
            TEST_PASS_DEBUG(m, "Allocated %zu bytes", sizes[i]);
            passed++;
        } else {
            TEST_FAIL(m, "Failed to allocate %zu bytes", sizes[i]);
            has_failures = true;
        }
    }
    
    // Verify allocated memory content before freeing
    for (i = 0; i < ARRAY_SIZE(sizes) && i < 10; i++) {
        if (ptrs[i]) {
            total++;
            data = (unsigned char *)ptrs[i];
            content_valid = true;
            
            // Check first few bytes
            for (j = 0; j < min_t(size_t, sizes[i], 10); j++) {
                if (data[j] != (i & 0xFF)) {
                    content_valid = false;
                    break;
                }
            }
            
            if (content_valid) {
                TEST_PASS_DEBUG(m, "Memory content verified for %zu bytes allocation", sizes[i]);
                passed++;
            } else {
                TEST_FAIL(m, "Memory content verification failed for %zu bytes allocation", sizes[i]);
                has_failures = true;
            }
        }
    }
    
    // Free every other object (create fragmentation pattern)
    for (i = 0; i < ARRAY_SIZE(sizes) && i < 10; i += 2) {
        if (ptrs[i]) {
            total++;
            l0_kfree(ptrs[i]);
            TEST_PASS_DEBUG(m, "Freed %zu bytes allocation", sizes[i]);
            ptrs[i] = NULL;
            passed++;
        }
    }
    
    // Allocate more objects to fill gaps
    for (i = 10; i < 15 && i < ARRAY_SIZE(ptrs); i++) {
        total++;
        ptrs[i] = l0_kmalloc(128, ctx->test_node_id);
        if (ptrs[i]) {
            memset(ptrs[i], 0xCC, 128);
            TEST_PASS_DEBUG(m, "Filled gap with 128 bytes");
            passed++;
        } else {
            TEST_FAIL(m, "Failed to fill gap with 128 bytes");
            has_failures = true;
        }
    }
    
    // Verify gap-filled memory content
    for (i = 10; i < 15 && i < ARRAY_SIZE(ptrs); i++) {
        if (ptrs[i]) {
            total++;
            data = (unsigned char *)ptrs[i];
            content_valid = true;
            
            for (j = 0; j < 128; j++) {
                if (data[j] != 0xCC) {
                    content_valid = false;
                    break;
                }
            }
            
            if (content_valid) {
                TEST_PASS_DEBUG(m, "Gap-filled memory content verified");
                passed++;
            } else {
                TEST_FAIL(m, "Gap-filled memory content verification failed");
                has_failures = true;
            }
        }
    }
    
    // Free all remaining
    for (i = 0; i < ARRAY_SIZE(ptrs); i++) {
        if (ptrs[i]) {
            total++;
            l0_kfree(ptrs[i]);
            TEST_PASS_DEBUG(m, "Final free of allocation %d", i);
            passed++;
        }
    }
    
    TEST_END(m, "Mixed allocation pattern test:  Passed: %d/%d operations", passed, total);
    
    // For strict validation, we require all operations to pass
    if (has_failures) {
        TEST_FAIL(m, "Some operations failed during mixed allocation patterns test");
        return -1;
    }
    
    return (passed == total && total > 0) ? 0 : -1;
}

/**
 * test_allocation_stress_pattern - Tests stress scenarios with many allocations
 * 
 * This test verifies:
 * 1. System behavior under memory pressure
 * 2. Stability during high allocation rates
 * 3. Handling of many small and large allocations
 * 4. Proper resource cleanup under stress
 * 5. Strict validation of all allocation and deallocation operations
 * 
 * Tests system stability under allocation stress with strict validation
 */
static int test_allocation_stress_pattern(struct seq_file *m)
{
    struct test_context *ctx = get_test_context();
    void *small_ptrs[50];
    void *large_ptrs[5];
    int i, j;
    int small_passed = 0, large_passed = 0;
    int total = 0;
    int small_freed = 0, large_freed = 0;
    bool has_failures = false;
    unsigned char *data;
    bool content_valid;
    
    // Initialize pointer arrays
    memset(small_ptrs, 0, sizeof(small_ptrs));
    memset(large_ptrs, 0, sizeof(large_ptrs));
    
    // Allocate many small objects
    for (i = 0; i < ARRAY_SIZE(small_ptrs); i++) {
        total++;
        small_ptrs[i] = l0_kmalloc(64, ctx->test_node_id);
        if (small_ptrs[i]) {
            // Verify memory is accessible and fill with pattern
            memset(small_ptrs[i], 0xDD, 64);
            small_passed++;
            TEST_PASS_DEBUG(m, "Allocated small object %d (64 bytes)", i);
        } else {
            TEST_FAIL(m, "Failed to allocate small object %d (64 bytes)", i);
            has_failures = true;
        }
    }
    
    // Verify content of small objects
    for (i = 0; i < ARRAY_SIZE(small_ptrs); i++) {
        if (small_ptrs[i]) {
            total++;
            data = (unsigned char *)small_ptrs[i];
            content_valid = true;
            
            // Check first few bytes
            for (j = 0; j < 10; j++) {
                if (data[j] != 0xDD) {
                    content_valid = false;
                    break;
                }
            }
            
            if (content_valid) {
                TEST_PASS_DEBUG(m, "Verified content of small object %d", i);
                small_passed++;
            } else {
                TEST_FAIL(m, "Content verification failed for small object %d", i);
                has_failures = true;
            }
        }
    }
    
    // Allocate some large objects
    for (i = 0; i < ARRAY_SIZE(large_ptrs); i++) {
        total++;
        large_ptrs[i] = l0_kmalloc(8192, ctx->test_node_id);
        if (large_ptrs[i]) {
            // Verify memory is accessible and fill with pattern (first 1KB)
            memset(large_ptrs[i], 0xEE, 1024);
            large_passed++;
            TEST_PASS_DEBUG(m, "Allocated large object %d (8192 bytes)", i);
        } else {
            TEST_FAIL(m, "Failed to allocate large object %d (8192 bytes)", i);
            has_failures = true;
        }
    }
    
    // Verify content of large objects
    for (i = 0; i < ARRAY_SIZE(large_ptrs); i++) {
        if (large_ptrs[i]) {
            total++;
            data = (unsigned char *)large_ptrs[i];
            content_valid = true;
            
            // Check first 1KB
            for (j = 0; j < 1024; j++) {
                if (data[j] != 0xEE) {
                    content_valid = false;
                    break;
                }
            }
            
            if (content_valid) {
                TEST_PASS_DEBUG(m, "Verified content of large object %d", i);
                large_passed++;
            } else {
                TEST_FAIL(m, "Content verification failed for large object %d", i);
                has_failures = true;
            }
        }
    }
    
    // Free all small objects
    for (i = 0; i < ARRAY_SIZE(small_ptrs); i++) {
        if (small_ptrs[i]) {
            total++;
            l0_kfree(small_ptrs[i]);
            small_ptrs[i] = NULL;
            small_freed++;
            TEST_PASS_DEBUG(m, "Freed small object %d", i);
        }
    }
    
    // Free all large objects
    for (i = 0; i < ARRAY_SIZE(large_ptrs); i++) {
        if (large_ptrs[i]) {
            total++;
            l0_kfree(large_ptrs[i]);
            large_ptrs[i] = NULL;
            large_freed++;
            TEST_PASS_DEBUG(m, "Freed large object %d", i);
        }
    }
    
    TEST_END(m, "Allocation stress pattern test:");
    TEST_END(m, "  Small allocations: %d/%d passed", small_passed/2, (int)ARRAY_SIZE(small_ptrs));
    TEST_END(m, "  Large allocations: %d/%d passed", large_passed/2, (int)ARRAY_SIZE(large_ptrs));
    TEST_END(m, "  Small objects freed: %d/%d", small_freed, (int)ARRAY_SIZE(small_ptrs));
    TEST_END(m, "  Large objects freed: %d/%d", large_freed, (int)ARRAY_SIZE(large_ptrs));
    TEST_END(m, "  Total operations: %d/%d passed", (small_passed + large_passed + small_freed + large_freed), total);
    
    // For strict validation, we require all operations to pass
    if (has_failures) {
        TEST_FAIL(m, "Some operations failed during stress pattern test");
        return -1;
    }
    
    // Check that we had some successful operations
    if ((small_passed + large_passed) > 0) {
        TEST_PASS(m, "Stress pattern test completed with %d successful allocations", 
                  small_passed/3 + large_passed/3);
        // Strict validation requires all operations to pass
        return (small_passed + large_passed + small_freed + large_freed) == total ? 0 : -1;
    } else {
        TEST_FAIL(m, "Stress pattern test failed - no allocations succeeded");
        return -1;
    }
}

/**
 * test_boundary_values - Tests allocation at boundary sizes
 * 
 * This test verifies:
 * 1. Minimum allocation size (1 byte)
 * 2. Size just below MIN_OBJ_SIZE (31 bytes)
 * 3. Exactly MIN_OBJ_SIZE (32 bytes)
 * 4. Exactly MAX_FIX_ALLOCATOR_SIZE (8192 bytes)
 * 5. Just above MAX_FIX_ALLOCATOR_SIZE (8193 bytes)
 * 6. Very large allocations near system limits
 * 7. Strict validation of all allocation and deallocation operations
 * 
 * Tests behavior at critical boundary values with strict validation
 */
static int test_boundary_values(struct seq_file *m)
{
    struct test_context *ctx = get_test_context();
    void *ptr;
    size_t boundary_sizes[] = {
        1,           // Minimum allocation
        31,          // Just below MIN_OBJ_SIZE
        32,          // Exactly MIN_OBJ_SIZE
        8192,        // Exactly MAX_FIX_ALLOCATOR_SIZE
        8193,        // Just above MAX_FIX_ALLOCATOR_SIZE
        (1UL << 31), // Large allocation near 2GB limit
    };
    int i, j;
    int passed = 0, total = 0;
    bool has_failures = false;
    unsigned char *data;
    bool content_valid;
    
    for (i = 0; i < ARRAY_SIZE(boundary_sizes); i++) {
        total += 2;
        ptr = l0_kmalloc(boundary_sizes[i], ctx->test_node_id);
        if (ptr) {
            // Verify memory is accessible
            size_t test_size = boundary_sizes[i] > 1024 ? 1024 : boundary_sizes[i];
            memset(ptr, 0xAA, test_size);
            
            // Verify content
            data = (unsigned char *)ptr;
            content_valid = true;
            for (j = 0; j < test_size; j++) {
                if (data[j] != 0xAA) {
                    content_valid = false;
                    break;
                }
            }
            
            if (content_valid) {
                TEST_PASS_DEBUG(m, "Boundary size %zu allocation and content verification successful", boundary_sizes[i]);
                passed++;
                
                // Free the allocated memory
                l0_kfree(ptr);
                TEST_PASS_DEBUG(m, "Boundary size %zu deallocation successful", boundary_sizes[i]);
                passed++;
            } else {
                TEST_FAIL(m, "Boundary size %zu content verification failed", boundary_sizes[i]);
                l0_kfree(ptr); // Still try to free
                has_failures = true;
            }
        } else {
            // For very large sizes, failure might be expected, so we don't count it as a failure
            if (boundary_sizes[i] < (1UL << 20)) { // For sizes less than 1MB, consider failure as an error
                has_failures = true;
            } else {
                passed += 2;
                TEST_INFO(m, "Boundary size %zu allocation failed (may be expected for very large sizes)", boundary_sizes[i]);
            }
        }
    }
    
    TEST_END(m, "Boundary value tests: %d/%d operations passed", passed, total);
    
    // For strict validation, we require all operations for reasonable sizes to pass
    if (has_failures) {
        TEST_FAIL(m, "Some operations failed during boundary value tests");
        return -1;
    }

    return (passed = total) ? 0 : -1;
}

// Export test cases
static struct test_case test_integration_cases[] = {
    {"Mixed Allocation Patterns", test_mixed_allocation_patterns},
    {"Allocation Stress Pattern", test_allocation_stress_pattern},
    {"Boundary Value Tests", test_boundary_values},
};

DECLARE_TEST_MODULE(integration, test_integration_cases)